package com.ddwl.order.service;

import com.ddwl.common.constant.*;
import com.ddwl.schema.bo.order.*;
import com.ddwl.schema.vo.order.RedisSeckillItemBo;
import com.ddwl.schema.vo.order.SeckillItemVo;
import com.ddwl.schema.vo.order.SeckillVo;
import com.ddwl.schema.vo.order.SecondKillOrderVo;
import org.springframework.util.CollectionUtils;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ddwl.common.exception.ParamException;
import com.ddwl.common.feign.api.GoodsApi;
import com.ddwl.common.util.BeanConverUtils;
import com.ddwl.common.util.MySqlUtil;
import com.ddwl.common.util.UUIDGenerator;
import com.ddwl.order.dao.SeckillDao;
import com.ddwl.order.dao.SeckillItemDao;
import com.ddwl.order.dao.SecondKillOrderDao;
import com.ddwl.order.dao.model.Seckill;
import com.ddwl.order.dao.model.SeckillItem;
import com.ddwl.order.dao.model.SecondKillOrder;
import com.ddwl.schema.bo.base.PageBo;
import com.ddwl.schema.bo.goods.ItemSginBo;
import com.ddwl.schema.util.DateUtil;
import com.ddwl.schema.vo.base.PageVo;
import com.ddwl.schema.vo.base.RestVo;
import com.ddwl.schema.vo.goods.ItemVo;
import com.ddwl.schema.vo.goods.SkuVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.gitee.tooleek.lock.spring.boot.annotation.Lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 秒杀service
 * @author wenbin
 */
@Service
@RefreshScope
@Slf4j
public class SeckillService {

    @Resource
    private SeckillDao seckillDao;
    @Resource
    private SeckillItemDao seckillItemDao;
    @Resource
    private GoodsApi goodsApi;
    @Resource
    private StringRedisTemplate template;
    @Resource
    private OrderInfoServer orderInfoServer;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private SecondKillOrderDao secondKillOrderDao;
    //删除标记
    private String delTag="delTag";

    /**
     * 新建秒杀活动
     * */
    @Transactional(rollbackFor = Throwable.class)
    public SeckillVo createSeckill(CreateSeckillBo bo) {

        ParamException.isFalse(!StringUtils.hasText(bo.getId()));
        ParamException.isNull(bo.getEndTime(),"结束时间不能为空");
        ParamException.isNull(bo.getStartTime(),"起始时间不能为空");
        ParamException.isTrue(DateUtil.getTimesMorning(bo.getStartTime(),1).compareTo(bo.getEndTime())<0,"秒杀活动不能跨天");
        if (bo.getIsEffect()==null) {
            bo.setIsEffect(0);
        }
        if (bo.getIsDist()==null) {
            bo.setIsDist(0);
        }
        ParamException.isTrue(bo.getStartTime()!=null
                &&bo.getEndTime()!=null&&bo.getStartTime().compareTo(bo.getEndTime())>=0,"开始时间不能大于结束时间");
        List<Seckill> seckills = seckillDao.selectAll();
        if (seckills!=null&&seckills.size()!=0){
            List<Seckill> list = seckills.stream().filter(o -> bo.getStartTime().compareTo(o.getStartTime()) > 0
                    && bo.getStartTime().compareTo(o.getEndTime()) < 0 || bo.getEndTime().compareTo(o.getStartTime()) > 0
                    && bo.getStartTime().compareTo(o.getEndTime()) < 0).collect(Collectors.toList());
            ParamException.isTrue(list!=null&&list.size()!=0,"活动时间不能重叠");
        }
        Seckill seckill = BeanConverUtils.convertBean(bo, Seckill.class, "id");
        seckill.setToken(UUID.randomUUID().toString());
        seckillDao.insert(seckill);
        SeckillVo seckillVo = BeanConverUtils.convertBean(seckill,SeckillVo.class);
        return seckillVo;
    }
    /**
     * 修改秒杀活动
     * */
    @Transactional(rollbackFor = Throwable.class)
    public SeckillVo updateSeckill(CreateSeckillBo bo) {
        ParamException.isTrue(!StringUtils.hasText(bo.getId()));
        Seckill seckill = BeanConverUtils.convertBean(bo, Seckill.class);
        if (bo.getEndTime()!=null||bo.getStartTime()!=null){
            List<Seckill> seckills = seckillDao.selectAll();
            if (seckills!=null&&seckills.size()!=0){
                if (bo.getEndTime()!=null){
                    List<Seckill> list = seckills.stream().filter(o -> bo.getEndTime().compareTo(o.getStartTime()) > 0
                            && bo.getStartTime().compareTo(o.getEndTime()) < 0&&(!o.getId().equals(bo.getId()))).collect(Collectors.toList());
                    ParamException.isTrue(list!=null&&list.size()!=0,"活动时间不能重叠");
                }
                if (bo.getEndTime()!=null){
                    List<Seckill> list = seckills.stream().filter(o -> bo.getEndTime().compareTo(o.getStartTime()) > 0
                            && bo.getStartTime().compareTo(o.getEndTime()) < 0&&(!o.getId().equals(bo.getId()))).collect(Collectors.toList());
                    ParamException.isTrue(list!=null&&list.size()!=0,"活动时间不能重叠");
                }
            }
        }

        SeckillVo seckillVo = BeanConverUtils.convertBean(seckillDao.selectByPrimaryKey(seckill.getId()),SeckillVo.class);
        ParamException.isTrue(seckillVo.getIsEffect()!=null&&seckillVo.getIsEffect()==1&&Instant.now().compareTo(seckillVo.getStartTime())>=0
                &&Instant.now().compareTo(seckillVo.getEndTime())<=0,"秒杀已开始不能修改");
        ParamException.isTrue(Instant.now().compareTo(seckillVo.getEndTime())>=0,"秒杀活动已结束不能修改");

        if (bo.getStartTime()!=null||bo.getEndTime()!=null) {
            seckill.setToken(UUID.randomUUID().toString());
        }
        seckillDao.updateByPrimaryKeySelective(seckill);
        SeckillVo seckillVo2 = BeanConverUtils.convertBean(seckillDao.selectByPrimaryKey(seckill.getId()),SeckillVo.class);

        ParamException.isTrue(seckillVo2.getStartTime()!=null
                &&seckillVo2.getEndTime()!=null&&seckillVo2.getStartTime().compareTo(seckillVo2.getEndTime())>=0,"开始时间不能大于结束时间");
        ParamException.isTrue(DateUtil.getTimesMorning(seckillVo2.getStartTime(),1).compareTo(seckillVo2.getEndTime())<0,"秒杀活动不能跨天");
        //修改秒杀活动更新已经秒杀数量为0
        QuerySeckillBo querySeckillBo=new QuerySeckillBo();
        querySeckillBo.setSeckillId(bo.getId());
        querySeckillBo.setSaleCount(0);
        seckillItemDao.updateSeckillItemSale(querySeckillBo);

        return seckillVo;
    }

    /**
     * 删除秒杀活动
     * */
    @Transactional(rollbackFor = Throwable.class)
    public void deleteSeckill(String seckillId) {
        ParamException.isTrue(!StringUtils.hasText(seckillId));
        SeckillVo seckillVo = BeanConverUtils.convertBean(seckillDao.selectByPrimaryKey(seckillId),SeckillVo.class);
        ParamException.isTrue(seckillVo==null,"秒杀活动id不存在");
/*        ParamException.isTrue(seckillVo.getIsEffect()==1&&Instant.now().compareTo(seckillVo.getStartTime())>=0
                &&Instant.now().compareTo(seckillVo.getEndTime())<=0,"秒杀已开始不能删除");*/
        releaseLockStock(seckillId);
        seckillDao.deleteById(seckillId);
    }

    /**
     * 新建秒杀商品
     * */
    @Transactional(rollbackFor = Throwable.class)
    public void createSeckillItem(List<CreateSeckillItemBo> list1) {
        String seckillId="";
        for (CreateSeckillItemBo bo:list1) {
            ParamException.isFalse(!StringUtils.hasText(bo.getId()),"秒杀活动商品行id不为空");
            ParamException.isTrue(!StringUtils.hasText(bo.getSeckillId()),"秒杀活动为空");
            ParamException.isBlank(bo.getBarcode(),"sku编码为空");
            if (!StringUtils.hasText(bo.getSkuId())){
                SkuVo skuVo = goodsApi.detailNew(bo.getBarcode()).getResult();
                if (skuVo!=null) {
                    bo.setSkuId(skuVo.getId());
                }
            }
            if (bo.getSeckillLimit()==null) {
                bo.setSeckillLimit(0);
            }
            if (bo.getSeckillCount()==null) {
                bo.setSeckillCount(0);
            }
            if (bo.getSeckillPrice()==null) {
                bo.setSeckillPrice(bo.getItemPrice());
            }
            if (bo.getOldSaleCount()==null) {
                bo.setOldSaleCount(0);
            }
            if (bo.getSeckillSort()==null) {
                bo.setSeckillSort(0);
            }
            SeckillItem seckillItem = BeanConverUtils.convertBean(bo, SeckillItem.class, "id");
            seckillItem.setSeckillSaleCount(0);
            seckillItem.setIsReleaseStock(0);
            seckillItemDao.insert(seckillItem);
            SeckillItemVo seckillItemVo = BeanConverUtils.convertBean(seckillItem,SeckillItemVo.class);

            //更新锁定库存
            LockStockBo lockStockBo=new LockStockBo();
            lockStockBo.setBarcode(seckillItemVo.getBarcode());
            lockStockBo.setOldLockStock(0);
            lockStockBo.setNewLockStock(bo.getSeckillCount());
            RestVo restVo = goodsApi.checkLockStock(lockStockBo);
            ParamException.isTrue(!restVo.getMsg().equals(""),restVo.getMsg());
            goodsApi.lockStock(lockStockBo);

            //更新商品标签
            setItemSign(seckillItemVo.getItemId(),"");


            seckillId = seckillItemVo.getSeckillId();
        }
        if (!seckillId.equals("")){
            //同一个秒杀活动同一种商品只能有一个
            QuerySeckillBo querySeckillBo=new QuerySeckillBo();
            querySeckillBo.setSeckillId(seckillId);
            List<Map> list = seckillItemDao.checkSeckillItemQty(querySeckillBo);
            list.stream().forEach(map -> {
                ParamException.isTrue(map.get("num")!=null
                        &&Integer.parseInt(String.valueOf(map.get("num")))>1,"一个秒杀活动同一种商品只能有一个");
            });
        }
    }

    /**
     * 更新商品标签
     */
    private void setItemSign(String itemId,String tag){
        ItemVo itemVo = goodsApi.getItemInfo(itemId).getResult();
        if (delTag.equals(tag)){
            if (!CollectionUtils.isEmpty(itemVo.getItemSgins())){
                List<String> list = itemVo.getItemSgins().stream().filter(o -> !"秒杀".equals(o)).collect(Collectors.toList());
                ItemSginBo itemSginBo=new ItemSginBo();
                List<String> itemIds=new ArrayList<>();
                itemIds.add(itemId);
                itemSginBo.setItemIds(itemIds);
                itemSginBo.setItemSgins(list);
                goodsApi.itemSginOperate(itemSginBo);
            }
        }else{
            if (!CollectionUtils.isEmpty(itemVo.getItemSgins())){
                List<String> list = itemVo.getItemSgins().stream().filter(o -> "秒杀".equals(o)).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(list)){
                    ItemSginBo itemSginBo=new ItemSginBo();
                    List<String> itemSgins=itemVo.getItemSgins();
                    List<String> itemIds=new ArrayList<>();
                    itemIds.add(itemId);
                    itemSgins.add("秒杀");
                    itemSginBo.setItemIds(itemIds);
                    itemSginBo.setItemSgins(itemSgins);
                    goodsApi.itemSginOperate(itemSginBo);
                }
            }else {
                ItemSginBo itemSginBo=new ItemSginBo();
                List<String> itemSgins=new ArrayList<>();
                List<String> itemIds=new ArrayList<>();
                itemIds.add(itemId);
                itemSgins.add("秒杀");
                itemSginBo.setItemIds(itemIds);
                itemSginBo.setItemSgins(itemSgins);
                goodsApi.itemSginOperate(itemSginBo);
            }
        }
    }

    /**
     * 修改秒杀商品
     * */
    @Transactional(rollbackFor = Throwable.class)
    public void updateSeckillItem(List<CreateSeckillItemBo> list1) {
        String seckillId="";
        for (CreateSeckillItemBo bo:list1) {
            ParamException.isTrue(!StringUtils.hasText(bo.getId()));

            if (bo.getSeckillCount()!=null){
                SeckillItem seckillItem1 = seckillItemDao.selectByPrimaryKey(bo.getId());
                //更新锁定库存
                LockStockBo lockStockBo=new LockStockBo();
                lockStockBo.setBarcode(seckillItem1.getBarcode());
                lockStockBo.setOldLockStock(seckillItem1.getSeckillCount());
                lockStockBo.setNewLockStock(bo.getSeckillCount());
                RestVo restVo = goodsApi.checkLockStock(lockStockBo);
                ParamException.isTrue(!restVo.getMsg().equals(""),restVo.getMsg());
                goodsApi.lockStock(lockStockBo);
            }

            SeckillItem seckillItem = BeanConverUtils.convertBean(bo, SeckillItem.class);
            seckillItem.setSeckillSaleCount(0);
            seckillItemDao.updateByPrimaryKeySelective(seckillItem);
            SeckillItemVo seckillItemVo = BeanConverUtils.convertBean(seckillItemDao.selectByPrimaryKey(seckillItem.getId()),SeckillItemVo.class);

            ParamException.isTrue(seckillItemVo==null,"id不存在");

            seckillId = seckillItemVo.getSeckillId();
        }
        if (!seckillId.equals("")){
            //校验一个秒杀活动同一种商品只能有一个
            QuerySeckillBo querySeckillBo=new QuerySeckillBo();
            querySeckillBo.setSeckillId(seckillId);
            List<Map> list = seckillItemDao.checkSeckillItemQty(querySeckillBo);
            list.stream().forEach(map -> {
                ParamException.isTrue(map.get("num")!=null
                        &&Integer.parseInt(String.valueOf(map.get("num")))>1,"一个秒杀活动同一种商品只能有一个");
            });
            Seckill seckill = seckillDao.selectByPrimaryKey(seckillId);
            ParamException.isTrue(seckill.getIsEffect()==1&&Instant.now().compareTo(seckill.getStartTime())>=0
                    &&Instant.now().compareTo(seckill.getEndTime())<=0,"秒杀已开始不能修改");
            ParamException.isTrue(Instant.now().compareTo(seckill.getEndTime())>=0,"秒杀活动已结束不能修改");
        }
    }

    /**
     * 删除秒杀商品
     * */
    @Transactional(rollbackFor = Throwable.class)
    public void deleteSeckillItem(String seckillItemId) {
        ParamException.isTrue(!StringUtils.hasText(seckillItemId));
        SeckillItemVo seckillItemVo = BeanConverUtils.convertBean(seckillItemDao.selectByPrimaryKey(seckillItemId),SeckillItemVo.class);
        ParamException.isTrue(seckillItemVo==null,"秒杀商品行id不存在");
        Seckill seckill = seckillDao.selectByPrimaryKey(seckillItemVo.getSeckillId());
/*        ParamException.isTrue(seckill.getIsEffect()==1&&Instant.now().compareTo(seckill.getStartTime())>=0
                &&Instant.now().compareTo(seckill.getEndTime())<=0,"秒杀已开始不能删除");*/
        seckillItemDao.deleteById(seckillItemId);

        //修改标签
        setItemSign(seckillItemVo.getItemId(),delTag);

        //释放锁定库存
        LockStockBo lockStockBo=new LockStockBo();
        lockStockBo.setBarcode(seckillItemVo.getBarcode());
        lockStockBo.setOldLockStock(seckillItemVo.getSeckillCount());
        lockStockBo.setNewLockStock(0);
        RestVo restVo = goodsApi.checkLockStock(lockStockBo);
        ParamException.isTrue(!restVo.getMsg().equals(""),restVo.getMsg());
        goodsApi.lockStock(lockStockBo);
    }


    /**
     * 分页查询秒杀活动
     * @param boPageBo
     * @return
     */
    public PageVo<SeckillVo> getSeckillPage(PageBo<QuerySeckillBo> boPageBo){

        PageHelper.startPage(boPageBo.getPage(),boPageBo.getPageSize(), MySqlUtil.orderBy(boPageBo.getSort(),boPageBo.isAsc()));
        PageInfo<Seckill> info = new PageInfo(seckillDao.querySeckillList(boPageBo.getParam()));
        if (boPageBo.getParam().getIsSeckillList()!=null&&boPageBo.getParam().getIsSeckillList()==1){
            List<Seckill> listNew=new ArrayList<>();

            boPageBo.getParam().setTime(Instant.now());
            boPageBo.getParam().setIsEffect(BaseEnum.StatusEnum.ENABLE.getCode());
            List<Seckill> list = seckillDao.querySeckillList(boPageBo.getParam());
            listNew.addAll(list);
            QuerySeckillBo querySeckillBo=new QuerySeckillBo();
            querySeckillBo.setStartTime(boPageBo.getParam().getTime());
            querySeckillBo.setEndTime(DateUtil.getTimesNight());
            querySeckillBo.setIsEffect(BaseEnum.StatusEnum.ENABLE.getCode());
            List<Seckill> seckills = seckillDao.querySeckillList(querySeckillBo);
            listNew.addAll(seckills);
            List<Seckill> list1 = listNew.stream().sorted((s1, s2) -> {
                return s1.getStartTime().compareTo(s2.getStartTime());
            }).collect(Collectors.toList());

            info.setList(list1);
        }
        List<SeckillVo> list = BeanConverUtils.convertBeanList(info.getList(),SeckillVo.class);
        return new PageVo<>(info.getTotal(),boPageBo.getPage(),boPageBo.getPageSize(),list);
    }

    /**
     * 查询当前进行的活动
     * @return
     */
    public SeckillVo getSeckillNow(){
        Seckill seckill=seckillDao.getRecentSeckill();
        if(null!=seckill){
            return BeanConverUtils.convertBean(seckill, SeckillVo.class);
        }
        return null;
    }


    /**
     * 分页查询秒杀商品
     * @param boPageBo
     * @return
     */
    public PageVo<SeckillItemVo> getSeckillItemPage(PageBo<QuerySeckillBo> boPageBo){
        if (boPageBo.getParam().getIsSeckillList()!=null&&boPageBo.getParam().getIsSeckillList()==1){
            boPageBo.setPageSize(500);
        }
        ParamException.isNull(boPageBo.getParam().getSeckillId(),"秒杀活动id不能为空");
        PageHelper.startPage(boPageBo.getPage(),boPageBo.getPageSize(), MySqlUtil.orderBy(boPageBo.getSort(),boPageBo.isAsc()));
        PageInfo<SeckillItem> info = new PageInfo(seckillItemDao.querySeckillItemList(boPageBo.getParam()));
        List<SeckillItemVo> list = BeanConverUtils.convertBeanList(info.getList(),SeckillItemVo.class);
        SeckillVo seckillVo = BeanConverUtils.convertBean(seckillDao.selectByPrimaryKey(boPageBo.getParam().getSeckillId()),SeckillVo.class);
        Map<String,SeckillItemVo> map=new HashMap<>();
        list.stream().forEach(seckillItemVo -> {
            SkuVo skuVo = goodsApi.detailNew(seckillItemVo.getBarcode()).getResult();
            seckillItemVo.setSkuVo(skuVo);
            if (seckillVo!=null){
                seckillItemVo.setStartTime(seckillVo.getStartTime());
                seckillItemVo.setEndTime(seckillVo.getEndTime());
            }
        });

        if (boPageBo.getParam().getIsSeckillList()!=null&&boPageBo.getParam().getIsSeckillList()==1){
            //计算出剩余数量
            list.stream().forEach(o->{
                o.setLastCount(o.getSeckillCount()-o.getSeckillSaleCount());
            });
            //剩余数量从小到大排序
            list = list.stream().sorted(Comparator.comparing(SeckillItemVo::getLastCount)).collect(Collectors.toList());
            //去重复sku
            for (SeckillItemVo seckillItemVo:list) {
                map.put(seckillItemVo.getItemId()+seckillItemVo.getSeckillId(),seckillItemVo);
            }
            Collection<SeckillItemVo> values = map.values();
            list = new ArrayList<>(values);
        }
        return new PageVo<>(info.getTotal(),boPageBo.getPage(),boPageBo.getPageSize(),list);
    }

    /**
     * 查询秒杀商品(商品明细用)
     * @param bo
     * @return
     */
    public List<SeckillItemVo> getSeckillItemDetail(QuerySeckillBo bo){

        if (!StringUtils.hasText(bo.getSeckillId())){
            bo.setTime(Instant.now());
        }
        ParamException.isNull(bo.getItemId(),"商品id不能为空");
        List<SeckillItemVo> seckillItemVos = seckillItemDao.querySeckillItem(bo);
        if (bo.getTime()!=null){
            if (seckillItemVos==null||seckillItemVos.size()==0){
                //查询今天的剩下的活动
                QuerySeckillBo querySeckillBo=new QuerySeckillBo();
                querySeckillBo.setStartTime(bo.getTime());
                querySeckillBo.setEndTime(DateUtil.getTimesNight());
                querySeckillBo.setIsEffect(1);
                List<Seckill> seckills = seckillDao.querySeckillList(querySeckillBo);
                if (seckills!=null&&seckills.size()!=0){
                    for (Seckill seckill :seckills) {
                        QuerySeckillBo querySeckillBo1=new QuerySeckillBo();
                        querySeckillBo1.setSeckillId(seckill.getId());
                        querySeckillBo1.setItemId(bo.getItemId());
                        List<SeckillItemVo> seckillItemVos1 = seckillItemDao.querySeckillItem(querySeckillBo1);
                        if (seckillItemVos1!=null&&seckillItemVos1.size()!=0){
                            return seckillItemVos1;
                        }

                    }
                }
            }
        }
        return seckillItemVos;
    }

    /**
     * 上架秒杀商品定时任务:上架最近3天的秒杀商品
     */
    @Deprecated
    public void uploadSeckillItem(){
        //1:查询最近3天需要秒杀的活动
        List<SeckillVo> seckillVos = querySeckillItem();
        //缓存活动信息
        redisSeckill(seckillVos);
        //缓存活动商品信息
        redisSeckillItem(seckillVos);

    }


    /**
     * 查询最近3天的秒杀活动包括商品
     */
    public List<SeckillVo> querySeckillItem(){
        //1:查询最近3天需要秒杀的活动
        QuerySeckillBo querySeckillBo=new QuerySeckillBo();
        querySeckillBo.setEndTime(DateUtil.getTimesNight(3));
        querySeckillBo.setStartTime(DateUtil.getTimesMorning(1));
        querySeckillBo.setIsEffect(1);
        List<SeckillVo> seckills = BeanConverUtils.convertBeanList(seckillDao.querySeckillList(querySeckillBo),SeckillVo.class);
        if (seckills!=null&&seckills.size()!=0){
            List<SeckillVo> collect = seckills.stream().map(o -> {
                QuerySeckillBo bo=new QuerySeckillBo();
                bo.setSeckillId(o.getId());
                List<SeckillItemVo> seckillItems = BeanConverUtils.convertBeanList(seckillItemDao.querySeckillItemList(bo),SeckillItemVo.class);
                o.setSeckillItems(seckillItems);
                return o;
            }).collect(Collectors.toList());
            return collect;
        }
        return null;
    }

    /**
     * 缓存活动信息
     */
    public void redisSeckill(List<SeckillVo> seckillVos){
        seckillVos.stream().forEach(seckillVo -> {
            Long startTime = seckillVo.getStartTime().toEpochMilli();
            Long endTime = seckillVo.getEndTime().toEpochMilli();
            String key= RedisKey.SECKILL_ACTIVE_CACHE_PREFIX.getKey(startTime+"_"+endTime);
            List<String> list = seckillVo.getSeckillItems().stream().map(SeckillItemVo::getBarcode).collect(Collectors.toList());
            //缓存活动信息
            template.opsForList().leftPushAll(key,list);
        });
    }
    /**
     * 缓存活动商品信息
     */
    public void redisSeckillItem(List<SeckillVo> seckillVos){
        seckillVos.stream().forEach(seckillVo -> {

            BoundHashOperations<String, Object, Object> ops = template.boundHashOps(RedisKey.SECKILL_ITEM_CACHE_PREFIX.getKey());
            seckillVo.getSeckillItems().stream().forEach(seckillItemVo -> {
                RedisSeckillItemBo redisSeckillItemBo;
                //缓存商品
                //1.sku秒杀信息
                redisSeckillItemBo=BeanConverUtils.convertBean(seckillItemVo,RedisSeckillItemBo.class);
                //2.sku基本信息
                SkuVo skuVo = goodsApi.detailNew(seckillItemVo.getBarcode()).getResult();
                redisSeckillItemBo.setSkuVo(skuVo);
                redisSeckillItemBo.setStartTime(seckillVo.getStartTime());
                redisSeckillItemBo.setEndTime(seckillVo.getEndTime());
                //3.token
                String token = UUID.randomUUID().toString().replace("-", "");
                redisSeckillItemBo.setRandomCode(token);

                String s = JSON.toJSONString(redisSeckillItemBo);
                ops.put(seckillItemVo.getId(),s);

            });

        });
    }

    /**
     * 查询秒杀订单
     * @param secondKillOrderId 秒杀订单ID
     * @return
     */
    public SecondKillOrderVo querySecondKillOrder(String secondKillOrderId){
        return BeanConverUtils.convertBean(secondKillOrderDao.selectByPrimaryKey(secondKillOrderId),SecondKillOrderVo.class);
    }


    /**
     * 快速创建秒杀订单
     */
   // @Lock(leaseTime = 10)
    @Transactional(rollbackFor = Exception.class)
    public String createSeckillOrder(SubmitSeckillItemBo bo, String userId, String product, String userName){
        //检查是否能下单
        SeckillItem seckillItem = checkCreateSeckillOrder(bo, userId);
        //创建秒杀订单
        SecondKillOrder secondKillOrder = createSecondKillOrder(bo, userId, seckillItem);
        //扣除活动库存
        seckillItemDao.lockSeckillItemQty(secondKillOrder.getSeckillId(),secondKillOrder.getSeckillSkuCode(),secondKillOrder.getSkuQty());
        //创建活动下单对象
        CreateSeckillOrderBo createSeckillOrderBo = createSeckillOrderBo(bo, userId, product, userName, seckillItem, secondKillOrder);
        //异步通知创建真实订单
        rabbitTemplate.convertAndSend(RabbitMqConstants.FAST_SECKILL_ORDER_QUEUE,JSONObject.toJSONString(createSeckillOrderBo));
        return secondKillOrder.getId();
    }

    private SeckillItem checkCreateSeckillOrder(SubmitSeckillItemBo bo, String userId) {
        Seckill seckill = seckillDao.selectByPrimaryKey(bo.getSeckillId());
        ParamException.isNull(seckill,"活动不存在");
        ParamException.isFalse(seckill.getIsEffect().equals(BaseEnum.StatusEnum.ENABLE.getCode()),"活动还未开始");
        ParamException.isTrue( seckill.getEndTime().compareTo(Instant.now())<0,"秒杀活动已结束");
        ParamException.isTrue( seckill.getStartTime().compareTo(Instant.now())>0,"秒杀活动未开始");
        SeckillItem seckillItem = seckillItemDao.querySeckillItemByBarCodeAndSeckillId(bo.getBarcode(),bo.getSeckillId());
        ParamException.isNull(seckillItem,"秒杀商品不存在");
        ParamException.isTrue(seckillItem.getSeckillCount()<bo.getSeckillQty()+seckillItem.getSeckillSaleCount(),"库存不足");
        List<SecondKillOrder> secondKillOrders = secondKillOrderDao.getByIdAndkillIdAndBarCode(userId,bo.getSeckillId(),seckillItem.getBarcode());
        Integer userKillCount = 0;
        if(!CollectionUtils.isEmpty(secondKillOrders)){
            userKillCount = secondKillOrders.stream().filter(o->!OrderEnum.PayStatus.FILL.getCode().equals(o.getCreStatus())).mapToInt(SecondKillOrder::getSkuQty).sum();
        }
        ParamException.isTrue(userKillCount+bo.getSeckillQty()>seckillItem.getSeckillLimit(),"超过限购数量");
        return seckillItem;
    }

    private CreateSeckillOrderBo createSeckillOrderBo(SubmitSeckillItemBo bo, String userId, String product, String userName, SeckillItem seckillItem, SecondKillOrder secondKillOrder) {
        CreateSeckillOrderBo createSeckillOrderBo = new CreateSeckillOrderBo();
        createSeckillOrderBo.setProduct(product);
        createSeckillOrderBo.setUserId(userId);
        createSeckillOrderBo.setUserName(userName);
        createSeckillOrderBo.setMasterOrderId(secondKillOrder.getId());
        createSeckillOrderBo.setSeckillPrice(secondKillOrder.getSkuPrice());
        createSeckillOrderBo.setSeckillQty(secondKillOrder.getSkuQty());
        createSeckillOrderBo.setItemId(seckillItem.getItemId());
        createSeckillOrderBo.setSkuId(seckillItem.getSkuId());
        createSeckillOrderBo.setSource(bo.getSource());
        createSeckillOrderBo.setBarcode(bo.getBarcode());
        createSeckillOrderBo.setUserAdderId(bo.getUserAdderId());
        createSeckillOrderBo.setSeckillId(bo.getSeckillId());
        createSeckillOrderBo.setDeliveryType(bo.getDeliveryType());
        createSeckillOrderBo.setUserMobile(bo.getUserMobile());
        createSeckillOrderBo.setRemark(bo.getRemark());
        return createSeckillOrderBo;
    }

    private SecondKillOrder createSecondKillOrder(SubmitSeckillItemBo bo, String userId, SeckillItem seckillItem) {
        SecondKillOrder secondKillOrder = new SecondKillOrder();
        secondKillOrder.setUserId(userId);
        secondKillOrder.setSeckillId(seckillItem.getSeckillId());
        secondKillOrder.setId(UUIDGenerator.getTimeUUID());
        secondKillOrder.setSkuPrice(seckillItem.getSeckillPrice());
        secondKillOrder.setSkuQty(bo.getSeckillQty());
        secondKillOrder.setSeckillSkuCode(bo.getBarcode());
        secondKillOrder.setCreStatus(OrderEnum.PayStatus.CRE.getCode());
        secondKillOrderDao.insert(secondKillOrder);
        return secondKillOrder;
    }

    /**
     * 接受秒杀订单处理结果
     * @param killOrderId 秒杀订单ID
     * @param orderId 业务订单ID
     * @param status 状态 20 成功 30失败
     */
    @Async
    public void secondKillOrderNotify(String killOrderId,String orderId,Integer status){
        log.info("接受秒杀订单通知消息:killOrderId={},orderId={},status={}",killOrderId,orderId,status);
        SecondKillOrder secondKillOrder = null;
        if(StrUtil.isNotBlank(killOrderId)){
            secondKillOrder = secondKillOrderDao.selectByPrimaryKey(killOrderId);
        }
        if(secondKillOrder == null && StrUtil.isNotBlank(orderId)){
            secondKillOrder = secondKillOrderDao.getByOrderId(orderId);
        }
        if(secondKillOrder==null){
            return;
        }
        SecondKillOrder update = new SecondKillOrder();
        update.setId(secondKillOrder.getId());
        update.setOrderNo(orderId);
        update.setCreStatus(status);
        secondKillOrderDao.updateByPrimaryKeySelective(update);
        //如果失败释放库存
        if(OrderEnum.PayStatus.FILL.getCode().equals(status)){
            seckillItemDao.releaseLockStock(secondKillOrder.getSeckillId(),secondKillOrder.getSeckillSkuCode(),secondKillOrder.getSkuQty());
        }
    }

    /**
     * 根据秒杀单创建订单
     * @param createSeckillOrderBo
     * @return
     */
    @Lock(leaseTime=10)
    @Transactional(rollbackFor = Exception.class)
    public String fastCreateOrder(CreateSeckillOrderBo createSeckillOrderBo){
        ParamException.isNull(createSeckillOrderBo.getMasterOrderId(),"订单id为空");
        Seckill seckill = seckillDao.selectByPrimaryKey(createSeckillOrderBo.getSeckillId());
        //构建
        CreateOrderBo createOrderBo=new CreateOrderBo();
        createOrderBo.setMasterOrderId(createSeckillOrderBo.getMasterOrderId());
        if (seckill.getIsDist()!=null&&seckill.getIsDist()==1){
            createOrderBo.setSource(createSeckillOrderBo.getSource());
        }
        createOrderBo.setUserAdderId(createSeckillOrderBo.getUserAdderId());
        createOrderBo.setOperator(createSeckillOrderBo.getUserId());
        createOrderBo.setRemark(createSeckillOrderBo.getRemark());
     //   createOrderBo.setPrice(createSeckillOrderBo.getSeckillPrice().multiply(new BigDecimal(createSeckillOrderBo.getSeckillQty())));
        CreateOrderBo.SkuList skuList=new CreateOrderBo.SkuList();
        skuList.setSkuId(createSeckillOrderBo.getSkuId());
        skuList.setSkuQty(createSeckillOrderBo.getSeckillQty());
        skuList.setUserMobile(createSeckillOrderBo.getUserMobile());
        skuList.setDeliveryType(OrderEnum.DeliveryType.KD.getCode());
        if(createSeckillOrderBo.getDeliveryType()!= null ){
            skuList.setDeliveryType(createSeckillOrderBo.getDeliveryType());
        }
        skuList.setActivityId(createSeckillOrderBo.getMasterOrderId());
        skuList.setUserAdderId(createSeckillOrderBo.getUserAdderId());
        skuList.setRemark(createSeckillOrderBo.getRemark());
        List<CreateOrderBo.SkuList> skuLists=new ArrayList<>();
        skuLists.add(skuList);
        createOrderBo.setSkuLists(skuLists);
        createOrderBo.setOrderType(1);
        createOrderBo.setProduct(createSeckillOrderBo.getProduct());
        return orderInfoServer.createOrderApi(createOrderBo);
    }

    /**
     * 释放秒杀商品库存
     */
    public void releaseLockStock(String seckillId) {
        if (seckillId==null||seckillId.equals("")){
            //查询前一天的秒杀活动
            Instant startTime = DateUtil.getTimesMorning(-1);
            Instant endTime = DateUtil.getTimesNight(-1);
            QuerySeckillBo querySeckillBo=new QuerySeckillBo();
            querySeckillBo.setStartTime(startTime);
            querySeckillBo.setEndTime(endTime);
            List<Seckill> seckills = seckillDao.querySeckillList(querySeckillBo);
            if(CollectionUtils.isEmpty(seckills)) {
                return;
            }
            List<String> seckillIdS= seckills.stream().map(Seckill::getId).collect(Collectors.toList());

            //查询所有需要释放商品
            QuerySeckillBo bo=new QuerySeckillBo();
            bo.setSeckillIds(seckillIdS);
            bo.setIsReleaseStock(0);
            List<SeckillItem> seckillItems = seckillItemDao.querySeckillItemList(bo);
            List<String> seckillItemsIds = seckillItems.stream().map(SeckillItem::getId).collect(Collectors.toList());
            seckillItems.stream().forEach(o->{
                //更新锁定库存
                LockStockBo lockStockBo=new LockStockBo();
                lockStockBo.setBarcode(o.getBarcode());
                lockStockBo.setOldLockStock(o.getSeckillCount());
                lockStockBo.setNewLockStock(0);
                RestVo restVo = goodsApi.checkLockStock(lockStockBo);
                ParamException.isTrue(!restVo.getMsg().equals(""),restVo.getMsg());
                goodsApi.lockStock(lockStockBo);
            });

            //释放后更改锁定状态
            QuerySeckillBo bo1=new QuerySeckillBo();
            if (seckillItemsIds!=null&&seckillItemsIds.size()!=0){
                bo1.setIds(seckillItemsIds);
                bo1.setIsReleaseStock(1);
                seckillItemDao.releaseLockStock(bo1);
            }
        }else {
            SeckillItem seckillItem=new SeckillItem();
            seckillItem.setSeckillId(seckillId);
            seckillItem.setIsReleaseStock(0);
            List<SeckillItem> seckillItems = seckillItemDao.select(seckillItem);

            seckillItems.stream().forEach(o->{
                //更新锁定库存
                LockStockBo lockStockBo=new LockStockBo();
                lockStockBo.setBarcode(o.getBarcode());
                lockStockBo.setOldLockStock(o.getSeckillCount());
                lockStockBo.setNewLockStock(0);
                RestVo restVo = goodsApi.checkLockStock(lockStockBo);
                ParamException.isTrue(!restVo.getMsg().equals(""),restVo.getMsg());
                goodsApi.lockStock(lockStockBo);
            });
            //释放后更改锁定状态
            QuerySeckillBo bo1=new QuerySeckillBo();
            bo1.setSeckillId(seckillId);
            bo1.setIsReleaseStock(1);
            seckillItemDao.releaseLockStock(bo1);
        }
    }


    public List<SeckillItemVo> getSeckillItemList(QuerySeckillBo bo) {
       return seckillItemDao.querySeckillItem(bo);
    }

    public void updateSeckillStatus(String id, Integer status) {
        ParamException.isBlank(id,"id为空");
        Seckill seckill = seckillDao.selectByPrimaryKey(id);
        ParamException.isTrue(Instant.now().compareTo(seckill.getEndTime())>=0,"秒杀活动已结束不能修改");
        seckill.setIsEffect(status);
        seckillDao.updateByPrimaryKeySelective(seckill);
    }
}

